/*
 * Written by edhong Feb. 2008
 *  sample arithmetic ENCODER using modified QccPack
 *  encodes a textfile consisting of only (ascii) 0's and 1's. 
 * Feb 2008
 *
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 *
 */

#include "libQccPack.h" 

#define USG_STRING "[-eof %d:EncodeEOF=0] [-tk %d:Use_tkfreq=1]  [-n %d:numContexts=1]  [-r %d:numRepeat=1] %s:tbitsfile %s:outbitsfile"
#define BLOCK_LENGTH  1

#define DEBUG 0

int encode_eof = 0;
int use_tkfreq = 1;
int numContexts = 1;  // # contexts to use; encode the same input file in every context. 
int numRepeat = 1;    // # times to repeatedly encode input before quitting. 
QccString tbitsfile;
QccString outbitsfile;


/* this function is for  making the error checking in main more readable */
void checkerror(int error, const char *message) {
  if (error) {
    char errormessage[1000];
    snprintf(errormessage, 1000, "Error Calling %s()", message); 
    QccErrorAddMessage(errormessage);
    QccErrorExit(); 
  } 
}

int main(int argc, char *argv[])
{
  
  QccInit(argc, argv);

  if (QccParseParameters(argc, argv,
                         USG_STRING,
                         &encode_eof,
                         &use_tkfreq,
			 &numContexts,
			 &numRepeat,
                         &tbitsfile,
                         &outbitsfile))
    QccErrorExit();

  if (encode_eof < 0  || encode_eof >  1)
    {
      QccErrorAddMessage("%s: encode_eof  must be 0 or 1",
                         argv[0]);
      QccErrorExit();
    }

  if (use_tkfreq < 0  || use_tkfreq >  1)
    {
      QccErrorAddMessage("%s: use_tkfreq  must be 0 or 1",
                         argv[0]);
      QccErrorExit();
    }

  if (numContexts <= 0 )
    {
      QccErrorAddMessage("%s: numContexts  must be >0",
                         argv[0]);
      QccErrorExit();
    }

  if (numRepeat <= 0 )
    {
      QccErrorAddMessage("%s: numRepeat  must be >0",
                         argv[0]);
      QccErrorExit();
    }

  if (!QccFileExists(tbitsfile)) {
      QccErrorAddMessage("%s: cannot find file %s ",
                         argv[0], tbitsfile);
      QccErrorExit();
  }

  QccString READMODE;
  QccConvertToQccString(READMODE, "r");
  FILE *tbitsfp = QccFileOpen(tbitsfile, READMODE);

  if (!QccFileSeekable(tbitsfp) && numRepeat > 1) 
    {
      QccErrorAddMessage("%s: numRepeat must be 1 with non-seekable file",
                         argv[0]);
      numRepeat = 1;
      QccErrorExit();
    }    
  
  QccBitBuffer output_buffer; 
  QccENTArithmeticModel *model = NULL; 

  QccBitBufferInitialize(&output_buffer); 
  QccConvertToQccString(output_buffer.filename, outbitsfile);

  output_buffer.type = QCCBITBUFFER_OUTPUT; 
  checkerror(QccBitBufferStart(&output_buffer), "QccBitBufferStart"); 
  
  int *num_symbols;
  num_symbols = (int *)malloc(sizeof(int)*numContexts);
  int i;
  for ( i=0; i < numContexts; i++) {
    num_symbols[i] = 2;
  }

  model = QccENTArithmeticEncodeStart2(num_symbols, 
				      numContexts, 
				      NULL, 
				      QCCENT_ANYNUMBITS, 
				      encode_eof 
				      ); 
  checkerror(model == NULL, "QccENTArithmeticEncodeStart2");

  if (use_tkfreq) 
    QccENTArithmeticSetModelAdaption(model, QCCENT_TKADAPTIVE);
  else
    QccENTArithmeticSetModelAdaption(model, QCCENT_ADAPTIVE);

  printf("Encoding from %s into %s.., %d contexts, %d copies, ", 
	 tbitsfile, outbitsfile, numContexts, numRepeat);
  if (encode_eof) printf("use_eof, ");
  else printf("no_eof, ");
  
  if (use_tkfreq) printf("use_tkfreq\n");
  else printf("normal_adapt\n");
  
  int x;
  int encodecount[2] = {0,0};

  for ( x =0; x < numRepeat; x++) {
    if (x > 0) {
      //      printf("Rewinding..\n");
      rewind(tbitsfp);      
    }

    while (!feof(tbitsfp)) {
      char temp;
      int errorvalue;
      errorvalue = QccFileReadChar(tbitsfp, &temp);
      if (errorvalue) {
	if (feof(tbitsfp)) break;
      }
      else {
	checkerror(errorvalue , "QccFileReadChar");
      }
      int nextbit = temp - '0';
      if (nextbit < 0 || nextbit > 1)
	{
	  QccErrorAddMessage("%s: illegal char %c in input tbits file",
			     argv[0], temp);
	  QccErrorExit();
	}
      
      int counter;

      for (counter = 0; counter < numContexts; counter++) {
	errorvalue = QccENTArithmeticSetModelContext(model, counter);
	checkerror(errorvalue, "QccENTArithmeticSetModelContext");
	
	errorvalue = QccENTArithmeticEncode(&nextbit, 
					    BLOCK_LENGTH, 
					    model, 
					    &output_buffer);
	checkerror(errorvalue,
		   "QccENTArithmeticEncode"); 
	if (DEBUG) {
		printf("encoded %d, context %d, 0-freq %d, 1-freq %d\n", 
		       nextbit, counter,
		       QccENTArithmeticGetSymbolFrequency(model, counter, 0),
		       QccENTArithmeticGetSymbolFrequency(model, counter, 1)
		       );	  
	}
		
	encodecount[nextbit]++;
      }
    }    
  }

  checkerror(QccFileClose(tbitsfp), "QccFileClose");

  double total = encodecount[0]+encodecount[1];
  printf("Encoded 1-bits: %d, 0-bits: %d, total: %d, prob: %g\n", encodecount[1], encodecount[0], (int) total, encodecount[1]/total);
  // this may or may not encode EOF, depending on model type. 
  checkerror(QccENTArithmeticEncodeEnd(model, 
				       0, // first_context..
				       &output_buffer), 
	     "QccENTArithmeticEncodeEnd"); 
  
  checkerror(QccBitBufferEnd(&output_buffer), 
	     "QccBitBufferEnd");
  
  QccENTArithmeticFreeModel(model); 

  printf("BitBuffer bit_count:\n %d\n", output_buffer.bit_cnt);

  QccExit; 

}
